package com.cugmarket.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cugmarket.constant.MessageConstant;
import com.cugmarket.dto.LoginFormDTO;
import com.cugmarket.dto.UserDTO;
import com.cugmarket.entity.ChatList;
import com.cugmarket.entity.ChatMessage;
import com.cugmarket.entity.Comment;
import com.cugmarket.entity.User;
import com.cugmarket.exception.BaseException;
import com.cugmarket.mapper.UserMapper;
import com.cugmarket.service.ChatListService;
import com.cugmarket.service.ChatMessageService;
import com.cugmarket.service.CommentService;
import com.cugmarket.service.UserService;
import com.cugmarket.utils.RegexUtils;
import com.cugmarket.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.cugmarket.constant.MessageConstant.*;
import static com.cugmarket.constant.RedisConstants.*;
import static com.cugmarket.constant.SystemConstants.*;

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserMapper userMapper;
    @Resource
    private ChatListService chatListService;
    @Resource
    private ChatMessageService chatMessageService;
    @Resource
    private CommentService commentService;
    /*
     * 发送验证码
     * @param phone
     * @return
     */
    @Override
    public String sendCode(String phone) {
        // 1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合，返回错误信息
            throw new BaseException(PHONE_ERROR);
        }
        // 3.符合，生成验证码
        String code = RandomUtil.randomNumbers(6);
        // 4.保存到redis set key code ex ttl
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
        // 5.发送验证码
        log.debug("发送短信验证码成功，验证码：{}",code);
        return code;
    }


    /**
     * 验证码登录
     * @param loginForm
     * @return
     */
    @Override
    public String loginWithCode(LoginFormDTO loginForm) {
        String phone = loginForm.getPhone();
        String code = loginForm.getCode();

        // 1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            throw new BaseException(MessageConstant.PHONE_ERROR);
        }
        // 2.校验验证码
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        if (cacheCode == null || !cacheCode.equals(code)) {
            throw new BaseException(MessageConstant.CODE_ERROR);
        }

        // 3.查询用户是否存在
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>().eq(User::getPhone, phone);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            // 4.用户不存在，创建新用户
            user = createUserByPhone(phone, DEFAULT_PASSWORD);
        }

        // 5.生成Token并返回
        return generateTokenForUser(user);
    }


    /**
     * 密码登录
     * @param loginForm
     * @return
     */
    @Override
    public String loginWithPassword(LoginFormDTO loginForm) {
        String phone = loginForm.getPhone();
        String password = loginForm.getPassword();

        // 1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            throw new BaseException(PHONE_ERROR);
        }
        // 2.查询用户
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>().eq(User::getPhone, phone);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            // 3.用户不存在，注册新用户
            user = createUserByPhone(phone, password);
        } else {
            // 4.对前端传来的密码进行MD5加密再与数据库中的密码进行比较
            String encryptedPassword = DigestUtils.md5DigestAsHex(password.getBytes());
            if (!user.getPassword().equals(encryptedPassword)) {
                // 5.用户存在但密码不匹配
                throw new BaseException(PASSWORD_ERROR);
            }
        }

        // 5.生成Token并返回
        return generateTokenForUser(user);
    }


    /**
     * 用户登出
     * @param token 登录令牌
     * @throws BaseException 如果登出失败则抛出异常
     */
    @Override
    public void logout(String token) {
        // 1. 检查 token 是否为空
        if (token == null || token.isEmpty()) {
            throw new BaseException(INVALID_TOKEN); // 无效的token
        }
        // 2. 构建 Redis 中存储用户信息的 key
        String tokenKey = LOGIN_USER_KEY + token;
        // 3. 删除 Redis 中的用户数据
        Boolean isDeleted = stringRedisTemplate.delete(tokenKey);
        // 4. 判断删除结果
        if (isDeleted == null || !isDeleted) {
            throw new BaseException(LOGOUT_FAILED); // 登出失败
        }
    }


    /**
     * 根据用户ID查询用户信息
     * @param userId 用户ID
     * @return 用户DTO
     */
    @Override
    public UserDTO getUserById(String userId) {
        // 使用MyBatis-Plus提供的selectById方法查询用户
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BaseException(USER_NOT_FOUND); // 用户不存在
        }
        // 属性拷贝，转换为UserDTO
        UserDTO userDTO = new UserDTO();
        BeanUtil.copyProperties(user, userDTO);
        return userDTO;
    }

    /**
     * 更新用户信息
     * @param userDTO 用户DTO
     */
    @Override
    @Transactional
    public void updateUser(UserDTO userDTO) {
        // 1.获取当前登录用户
        String userId = UserHolder.getUser();
        // 2.检查用户是否存在
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BaseException(USER_NOT_FOUND); // 用户不存在
        }
        // 3.获取user中的头像、昵称、省份
        String oldAvatar = user.getAvatar();
        String oldNickName = user.getNickName();
        // 使用属性拷贝将 userDTO 的属性复制到 User 实体类
        BeanUtil.copyProperties(userDTO, user);
        // 使用MyBatis-Plus提供的updateById方法根据userDTO中的id直接更新
        userMapper.updateById(user);
        // 联动更新ChatList,ChatMessage,Comment
        if (!oldAvatar.equals(userDTO.getAvatar())) {
            // ChatList
            // 更新fromUserAvatar
            LambdaUpdateWrapper<ChatList> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(ChatList::getFromUserAvatar, userDTO.getAvatar())
                    .eq(ChatList::getFromUserId, userId);
            chatListService.update(null, updateWrapper);
            // 更新toUserAvatar
            updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(ChatList::getToUserAvatar, userDTO.getAvatar())
                    .eq(ChatList::getToUserId, userId);
            chatListService.update(null, updateWrapper);

            // ChatMessage
            // 更新fromUserAvatar
            LambdaUpdateWrapper<ChatMessage> updateWrapper2 = new LambdaUpdateWrapper<>();
            updateWrapper2.set(ChatMessage::getFromUserAvatar, userDTO.getAvatar())
                    .eq(ChatMessage::getFromUserId, userId);
            chatMessageService.update(null, updateWrapper2);
            // 更新toUserAvatar
            updateWrapper2 = new LambdaUpdateWrapper<>();
            updateWrapper2.set(ChatMessage::getToUserAvatar, userDTO.getAvatar())
                    .eq(ChatMessage::getToUserId, userId);
            chatMessageService.update(null, updateWrapper2);
        }
        //
        if (!oldNickName.equals(userDTO.getNickName())) {
            // ChatList
            // 更新fromUserNick
            LambdaUpdateWrapper<ChatList> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(ChatList::getFromUserNick, userDTO.getNickName())
                    .eq(ChatList::getFromUserId, userId);
            chatListService.update(null, updateWrapper);
            // 更新toUserNick
            updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(ChatList::getToUserNick, userDTO.getNickName())
                    .eq(ChatList::getToUserId, userId);
            chatListService.update(null, updateWrapper);

            // ChatMessage
            // 更新fromUserNick
            LambdaUpdateWrapper<ChatMessage> updateWrapper2 = new LambdaUpdateWrapper<>();
            updateWrapper2.set(ChatMessage::getFromUserNick, userDTO.getNickName())
                    .eq(ChatMessage::getFromUserId, userId);
            chatMessageService.update(null, updateWrapper2);
            // 更新toUserNick
            updateWrapper2 = new LambdaUpdateWrapper<>();
            updateWrapper2.set(ChatMessage::getToUserNick, userDTO.getNickName())
                    .eq(ChatMessage::getToUserId, userId);
            chatMessageService.update(null, updateWrapper2);

            // Comment
            // 更新pub_nickname, pub_avatar
            LambdaUpdateWrapper<Comment> updateWrapper3 = new LambdaUpdateWrapper<>();
            updateWrapper3.set(Comment::getPubNickname, userDTO.getNickName()).set(Comment::getPubAvatar, userDTO.getAvatar())
                    .eq(Comment::getPubUserId, userId);
            commentService.update(null, updateWrapper3);

            // 查询所有被修改的Comment记录的ID
            LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Comment::getPubUserId, userId);
            List<Comment> comments = commentService.list(queryWrapper);
            List<String> commentIds = comments.stream()
                    .map(Comment::getId)
                    .collect(Collectors.toList());
            // 如果存在以该用户为父级评论的其他评论，则更新
            if (commentIds.size() > 0) {
                // 更新parent_nickname
                LambdaUpdateWrapper<Comment> updateWrapper4 = new LambdaUpdateWrapper<>();
                updateWrapper4.set(Comment::getParentNickname, userDTO.getNickName())
                        .in(Comment::getParentId, commentIds);
                commentService.update(null, updateWrapper4);
            }

        }
    }

    /**
     * 获取当前登录用户信息
     * @return UserDTO
     */
    @Override
    public UserDTO getUserInfo() {
        String userId = UserHolder.getUser();
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BaseException(USER_NOT_FOUND); // 用户不存在
        }
        // 属性拷贝，转换为UserDTO
        UserDTO userDTO = new UserDTO();
        BeanUtil.copyProperties(user, userDTO);
        return userDTO;
    }

    /**
     * 修改密码
     * @param password 新密码
     * @param confirmPassword 确认密码
     */
    @Override
    public void changePassword(String password, String confirmPassword) {
        // 1.校验两次密码是否一致
        if (!password.equals(confirmPassword)) {
            throw new BaseException(MessageConstant.NOT_MATCH_EROOR); // 密码不一致
        }
        // 2.获取当前登录用户
        String userId = UserHolder.getUser();
        // 3.检查用户是否存在
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BaseException(USER_NOT_FOUND); // 用户不存在
        }
        // 4.使用MD5加密密码
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        // 5.更新密码
        user.setPassword(password);
        userMapper.updateById(user);
    }


    /**
     * 生成Token并返回
     * @param user
     * @return
     */
    private String generateTokenForUser(User user) {
        String token = UUID.randomUUID().toString(true);
        // 2.将用户Id存储到Redis中
        String tokenKey = LOGIN_USER_KEY + token;
        stringRedisTemplate.opsForValue().set(tokenKey, user.getId());
        // 3.设置有效期
        stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
        // 4.返回Token
        return token;
    }



    /**
     * 创建新用户
     * @param phone
     * @param password
     * @return
     */
    private User createUserByPhone(String phone, String password) {
        User user = new User();
        user.setPhone(phone);
        // 使用MD5加密密码
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        user.setPassword(password); // 设定用户的初始密码
        user.setAvatar(DEFAULT_AVATAR);
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
        save(user);
        return user;
    }
}
